#include "stm32f1xx_hal.h"
#include "flash_on_chip.h"
#include "spi.h"
#include "usart.h"
#define AFIO_MAPR_MEM_MODE   (0x3U)
typedef struct {
    uint32_t magic;       // 魔术字，用来标识这个结构是否有效
    uint32_t fw_size;     // 新固件的大小（字节数），一般 ≤ 暂存区大小
    uint32_t fw_crc32;    // 新固件的 CRC32 校验值
    uint32_t committed;   // 升级标志：0x5AA55AA5 表示下次重启要执行覆盖
} ota_flag_t;

typedef void (*pFunction)(void);

void jump_to_app(void)
{
    uint32_t app_sp     = *(__IO uint32_t*) APP_START_ADDR;
    uint32_t app_entry  = *(__IO uint32_t*) (APP_START_ADDR + 4);
    pFunction JumpToApp = (pFunction) app_entry;

    printf("Jumping to APP...\r\n");
    printf("app_sp = 0x%08X, app_entry = 0x%08X\r\n", app_sp, app_entry);

    if ((app_sp & 0x2FFE0000) == 0x20000000)
    {
		// 关闭中断
        __disable_irq();

		// 关闭外设
		HAL_UART_DeInit(&huart1);
		HAL_SPI_DeInit(&hspi1);
        HAL_DeInit();

        // 设置向量表到APP起始
        SCB->VTOR = APP_START_ADDR;
        // 重新设置栈指针
        __set_MSP(app_sp);
        // 跳转执行
        JumpToApp();
    }
    else
    {
        printf("Invalid app stack pointer: 0x%08X\r\n", app_sp);
    }
}

static uint32_t crc32_full_at(uint32_t base, uint32_t size)
{
	uint32_t c = 0xFFFFFFFF;
	for (uint32_t i = 0; i < size; i++)
	{
		c ^= *(uint8_t *)(base + i);
		for (int j = 0; j < 8; j++)
		{
			c = (c & 1) ? (0xEDB88320u ^ (c >> 1)) : (c >> 1);
		}
	}
	return c ^ 0xFFFFFFFF;
}
/**
 * @brief  擦除 OTA 元数据（标志位）
 */
static void meta_clear(void)
{
	HAL_FLASH_Unlock();
	FLASH_EraseInitTypeDef er = {0};
	uint32_t e = 0;
	er.TypeErase = FLASH_TYPEERASE_PAGES;
	er.PageAddress = Flage_PAGE_ADDR;
	er.NbPages = 1;
	HAL_FLASHEx_Erase(&er, &e);
	HAL_FLASH_Lock();
}

/**
 * @brief  从 OTA 暂存区拷贝固件到 APP 区
 * @param  staging_addr 暂存区起始地址（例如 0x0800A000）
 * @param  size         固件长度（字节数）
 * @note   在 Bootloader 中运行，App 不可运行
 */
static void flash_swap_copy(uint32_t staging_addr, uint32_t size)
{
    HAL_FLASH_Unlock();

    uint32_t app_addr = APP_START_ADDR;
    uint32_t app_end  = APP_START_ADDR + size;

    /* 边界保护：避免写越界 OTA 区 */
    if (app_end > OTA_START_ADDR) {
        HAL_FLASH_Lock();
        return;
    }

    /* 1. 擦除 APP 区  */
    FLASH_EraseInitTypeDef erase;
    uint32_t page_error = 0;

    erase.TypeErase = FLASH_TYPEERASE_PAGES;
    erase.PageAddress = APP_START_ADDR;
    erase.NbPages = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;

    if (HAL_FLASHEx_Erase(&erase, &page_error) != HAL_OK) {
        HAL_FLASH_Lock();
        return;
    }

    /* 2. 从 OTA 区复制到 APP 区  */
    for (uint32_t addr = 0; addr < size; addr += 2)
    {
        uint16_t halfword;

        uint8_t b1 = *(uint8_t *)(staging_addr + addr);
        uint8_t b2 = (addr + 1 < size) ? *(uint8_t *)(staging_addr + addr + 1) : 0xFF;
        halfword = (uint16_t)(b1 | (b2 << 8));

        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, app_addr + addr, halfword) != HAL_OK)
        {
            HAL_FLASH_Lock();
            return;
        }
    }

    HAL_FLASH_Lock();
    meta_clear();
}

/**
 * @brief  OTA API：从暂存区拷贝固件到基址（APP 区）
 * @param  staging_addr 暂存区起始地址
 * @param  size         固件大小
 * @note   实际调用的就是 ram_swap_copy()，后者执行完成会复位 MCU。
 */
void OTA_SwapToBase_FromStaging(uint32_t staging_addr, uint32_t size)
{
    flash_swap_copy(staging_addr, size);
}

/**
 * @brief  读取 OTA 元数据（标志位）
 */
static int meta_read(ota_flag_t *m)
{
	memcpy(m, (void *)Flage_PAGE_ADDR, sizeof(*m));
	return (m->magic == 0xB00710AD && m->committed == 0x5AA55AA5) ? 0 : -1;
}

/**
 * @brief  OTA API：启动时检查并执行 OTA 升级（早期调用）
 * @note   该函数应在系统初始化早期调用，以确保在 Flash 被擦写时不会执行代码。
 */
void OTA_CheckAndSwap_Early(void)
{
	ota_flag_t m;
	if (meta_read(&m) != 0)
	{
		printf("no ota flag!\r\n");
		
	}
	else if (m.fw_size == 0 || m.fw_size > (OTA_END_ADDR - OTA_START_ADDR - PAGE_SIZE))
	{
		printf("flag data error! \r\n");
		meta_clear();
		
	}
	else if (crc32_full_at(OTA_START_ADDR, m.fw_size) != m.fw_crc32)
	{
		printf("crc32 error!\r\n");
		meta_clear();
	}
	else{
		printf("start copy ota data\r\n");
		OTA_SwapToBase_FromStaging(OTA_START_ADDR, m.fw_size);
	}
	jump_to_app();
}
